package com.qzl.feiq.server;

import cn.hutool.core.collection.CollectionUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.slf4j.Slf4j;
import com.qzl.feiq.config.FeiQCache;
import com.qzl.feiq.config.StaticConfig;
import com.qzl.feiq.enu.MessageTypeEnum;
import com.qzl.feiq.message.AdditionalSection;
import com.qzl.feiq.message.Message;
import com.qzl.feiq.protocol.UdpMessageCodec;
import com.qzl.feiq.protocol.UdpSimple2Message;
import com.qzl.feiq.strategy.Receive;
import com.qzl.feiq.strategy.ReceiveFactory;
import com.qzl.feiq.util.FeiQUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class UdpServer {
    public static void main(String[] args) throws InterruptedException {
        init();
        Scanner scanner = new Scanner(System.in);
        while (true){
            FeiQCache.udpChannelMap.forEach((ip, channel) -> {
                log.info("IP: {} - Channel active: {}", ip, channel.isActive());
            });
            String msg = scanner.nextLine();
            if (StringUtils.isBlank(msg)){
                continue;
            } else if (Objects.equals(msg, "q")){
                close();
                break;
            }
            sendMsg("192.168.88.132", msg);
        }
    }

    public static void start() throws InterruptedException {
//        List<String> ips = Utils.getLocalIpAddress();
        Set<String> ips = FeiQCache.allIps;
        for (String ip : ips) {
            try {
                NioEventLoopGroup group = new NioEventLoopGroup();
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(group)
                        .channel(NioDatagramChannel.class)
                        .option(ChannelOption.SO_BROADCAST, true) // 关键：启用广播
                        .option(ChannelOption.SO_REUSEADDR, true)// 关键：允许多IP重用同一端口
                        .localAddress(new InetSocketAddress(ip, StaticConfig.UDP_PORT))// 指定网卡IP
                        .handler(new ChannelInitializer<DatagramChannel>() {
                            @Override
                            protected void initChannel(DatagramChannel ch) throws Exception {
//                                ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                                // 添加自定义编解码器
                                ch.pipeline().addLast(new UdpSimple2Message());
                                ch.pipeline().addLast(new UdpMessageCodec());
                                // 添加自定义业务处理
                                ch.pipeline().addLast(new SimpleChannelInboundHandler<Message>() {
                                    @Override
                                    protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception {
                                        if (!"PC".equals(msg.getMsg())){
                                            log.info("用户:{}, ip:{}, 发来消息:{}", msg.getSenderName(), msg.getFromUserIp(), msg.getMessageType());
                                        }
                                        Receive receive = ReceiveFactory.getInvokeStrategy(msg.getCommand());
                                        if (receive == null){
                                            log.error("未找到对应的处理策略:{}", msg.getCommand());
                                        } else {
                                            receive.receiveMsg(ctx.channel(), msg);
                                        }
                                        // 对话消息
                                        if (msg.getCommand() == 288 ){
                                            FeiQCache.messageQueue.add(msg);
                                        }
                                        // 将 message 转发给下一个 handler
                                        ctx.fireChannelRead(msg);
                                    }
                                    @Override
                                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                        // 广播
                                        Message message = new Message(FeiQUtils.ipToBroadcast(ip), MessageTypeEnum.broadcast, StaticConfig.IPMSG_BR_ENTRY);
                                        ctx.writeAndFlush(message);
                                    }

                                    @Override
                                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                        log.error("发生异常：{}", cause.getMessage(), cause);
                                        ctx.close(); // 或重新连接
                                    }
                                });
                                ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                    @Override
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        // 此时已经拿到了 message对象
                                        Message message = (Message) msg;
                                        List<AdditionalSection> additionalSections = message.getAdditionalSections();
                                        // 当前handler只处理文件相关的数据
                                        if (CollectionUtil.isNotEmpty(additionalSections)){
                                            // 开启tcp 客户端服务

                                        }
                                    }
                                });
                            }
                        });
                Channel channel = bootstrap.bind().sync().channel();
                FeiQCache.udpChannelMap.put(ip, channel);
                FeiQCache.udpEventLoopGroupMap.put(ip, group);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(String closeIp) {
        FeiQCache.udpChannelMap.forEach((ip, channel) -> {
            try {
                // 关闭全部
                if (StringUtils.isBlank(closeIp)){
                    channel.closeFuture().sync();
                    FeiQCache.udpChannelMap.remove(ip);
                    FeiQCache.udpEventLoopGroupMap.get(ip).shutdownGracefully();
                    FeiQCache.udpEventLoopGroupMap.remove(ip);
                    // 发送广播退出
                    Message message = new Message(FeiQUtils.ipToBroadcast(ip), StaticConfig.IPMSG_BR_EXIT);
                    channel.writeAndFlush(message);
                } else if (Objects.equals(ip, closeIp)){
                    channel.closeFuture().sync();
                    FeiQCache.udpChannelMap.remove(ip);
                    FeiQCache.udpEventLoopGroupMap.get(ip).shutdownGracefully();
                    FeiQCache.udpEventLoopGroupMap.remove(ip);
                    // 发送广播退出
                    Message message = new Message(FeiQUtils.ipToBroadcast(ip), StaticConfig.IPMSG_BR_EXIT);
                    channel.writeAndFlush(message);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    public static void close() {
       close(null);
    }

    /**
     * 发送消息
     */
    public static void sendMsg(String toUserIp, String msg) {
        FeiQCache.udpChannelMap.forEach((ip, channel) -> {
            if (Objects.equals(FeiQUtils.ipSubString(ip), FeiQUtils.ipSubString(toUserIp))) {
                Message message = new Message(toUserIp, StaticConfig.IPMSG_SENDMSG | StaticConfig.IPMSG_SENDCHECKOPT);
                message.setMsg(msg);
                channel.writeAndFlush(message);
            }
        });
        //todo 有需要可做重试，发出后收到会有确认消息，标识为数据包编号，如果没收到确认可重发，保证消息送到
    }

    // 初始化网卡所有ip
    public static void init() throws InterruptedException {
        FeiQCache.allIps.add("192.168.88.1");
//        FeiQCache.allIps.add("192.168.66.1");
        start();

        // 定时任务广播，netty无法持续监听广播
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(() -> {
            FeiQCache.udpChannelMap.forEach((ip, channel) -> {
                String broadcastIp = FeiQUtils.ipToBroadcast(ip);
                Message msg = new Message(broadcastIp, MessageTypeEnum.broadcast, StaticConfig.IPMSG_BR_ENTRY);
                channel.writeAndFlush(msg);
//                log.info("广播探测已发送至: {}", broadcastIp);
            });
        }, 0, 10, TimeUnit.SECONDS); // 每10秒广播一次

    }
}
